package com.evil.application.enums.reporting;

import cn.hutool.core.map.MapUtil;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.pojo.dto.reporting.data.ReportingTableDataDTO;
import com.evil.common.application.dto.order.OrderPageDataRespDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.NumberUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 汇总方式
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@AllArgsConstructor
public enum SummaryTypeEnum {

    /**
     * 汇总方式
     */
    NO(0, "无",
            (orders, filter) -> Optional.empty(),
            tableDataList -> Optional.empty()
    ),
    COUNT(1, "计数",
            (orders, filter) -> SummaryTypeEnum.calculation(orders,
                    filter,
                    e -> null,
                    stream -> Optional.of(stream.count()).map(e -> e)
            ),
            tableDataList -> Optional.of(tableDataList.size())
    ),

    SUM(2, "求和",
            (orders, filter) -> SummaryTypeEnum.calculation(orders,
                    filter,
                    e -> NumberUtils.toBigDecimal(e, BigDecimal.ZERO),
                    stream -> stream.reduce((a, b) -> ((BigDecimal) a).add((BigDecimal) b))
            ),
            tableDataList -> tableDataList.stream().map(e -> NumberUtils.toBigDecimal(e.getValue(), BigDecimal.ZERO)).reduce(BigDecimal::add).map(e -> e)
    ),

    AVERAGE(3, "平均值",
            (orders, filter) -> SummaryTypeEnum.calculation(orders,
                    filter,
                    e -> NumberUtils.toBigDecimal(e, BigDecimal.ZERO),
                    stream -> SummaryTypeEnum.average(stream, e -> ((BigDecimal) e).doubleValue())
            ),
            tableDataList -> SummaryTypeEnum.average(tableDataList.stream(), e -> NumberUtils.toDouble(e.getValue(), null))
    ),

    MAX(4, "最大值",
            (orders, filter) -> SummaryTypeEnum.calculation(orders,
                    filter,
                    e -> NumberUtils.toBigDecimal(e, BigDecimal.ZERO),
                    stream -> stream.reduce((a, b) -> ((BigDecimal) a).max((BigDecimal) b))
            ),
            tableDataList -> tableDataList.stream().max(Comparator.comparing(ReportingTableDataDTO::getValue)).map(ReportingTableDataDTO::getValue)
    ),

    MIN(5, "最小值",
            (orders, filter) -> SummaryTypeEnum.calculation(orders,
                    filter,
                    e -> NumberUtils.toBigDecimal(e, BigDecimal.ZERO),
                    stream -> stream.reduce((a, b) -> ((BigDecimal) a).min((BigDecimal) b))
            ),
            tableDataList -> tableDataList.stream().min(Comparator.comparing(ReportingTableDataDTO::getValue)).map(ReportingTableDataDTO::getValue)
    ),

//    NEWEST_DATA(6, "最新数据",
//            (orders, filter) -> Optional.of(orders.stream().max(Comparator.comparingLong(e -> e.getUpdated().getTime()))),
//            tableDataList -> Optional.empty()
//    ),
//
//    OLDEST_DATA(7, "最旧数据",
//            (orders, filter) -> Optional.of(orders.stream().min(Comparator.comparingLong(e -> e.getUpdated().getTime()))),
//            tableDataList -> Optional.empty()
//    ),
    ;

    @Getter
    private final int id;
    @Getter
    private final String name;
    @Getter
    private final BiFunction<List<OrderPageDataRespDTO>, Predicate<ControlDataDTO>, Optional<Object>> calculation;
    @Getter
    private final Function<List<ReportingTableDataDTO>, Optional<Object>> summary;

    public static Optional<SummaryTypeEnum> findById(Integer id) {
        if (Objects.isNull(id)) {
            return Optional.empty();
        }
        return Arrays.stream(values()).filter(e -> e.id == id).findFirst();
    }

    public static SummaryTypeEnum isExistById(Integer id) {
        return SummaryTypeEnum.findById(id).orElseThrow(SummaryTypeEnum::getBusinessException);
    }

    public static Stream<SummaryTypeEnum> getStream() {
        return Arrays.stream(values());
    }

    public static List<Map<Integer, String>> types() {
        return SummaryTypeEnum.getStream().map(e -> MapUtil.of(e.id, e.name)).collect(Collectors.toList());
    }

    public static BusinessException getBusinessException() {
        return new BusinessException(RCodeEnum.ERROR_PARAMETER, "汇总方式参数错误");
    }

    public boolean hasSummary() {
        return NO.id != this.id;
    }

    public static <R> Optional<R> calculation(List<OrderPageDataRespDTO> orders,
                                              Predicate<ControlDataDTO> filter,
                                              Function<String, R> format,
                                              Function<Stream<R>, Optional<R>> reduce) {
        List<String> values = new ArrayList<>();

        orders.forEach(order -> {
            Stream<ControlDataDTO> stream = order.getData().stream();
            // 过滤
            if (Objects.nonNull(filter)) {
                stream = stream.filter(filter);
            }
            // 收集
            stream.forEach(controlData -> {
                ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(controlData.getControlCode());
                controlData.getValues().forEach(dataValue -> values.add(controlCodeEnum.getDataValueToString().apply(dataValue)));
            });
        });
        return reduce.apply(values.stream().map(format));
    }

    public static <T> Optional<Object> average(Stream<T> stream, ToDoubleFunction<T> toDouble) {
        OptionalDouble average = stream.mapToDouble(toDouble).average();
        if (average.isPresent()) {
            return Optional.of(average.getAsDouble());
        }
        return Optional.empty();
    }
}
